﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using Bzure.Security.Principal;

namespace Bzure.Security.Permissions.CAS
{
    [Serializable]
    public class BzurePermission : IPermission, IUnrestrictedPermission
    {

        protected IDroit _droit;
        protected bool _isUnrestricted;
        public BzurePermission(IDroit Droit)
        {
            _droit = Droit.Copy();
        }
        public BzurePermission(PermissionState state)
        {
            switch (state)
            {
                case PermissionState.None:
                    break;
                case PermissionState.Unrestricted:
                    _isUnrestricted = true;
                    break;
                default:
                    throw new ArgumentException("unknown PermissionState");
            }
        }
        public virtual BzurePrincipal Principal
        {
            get { return Thread.CurrentPrincipal as BzurePrincipal; }
        }
        public virtual IPermission Copy()
        {
            return new BzurePermission(this._droit.Copy());
        }

        public virtual void Demand()
        {
            StringBuilder sb = new StringBuilder();
            PermissionExceptionType EventType = PermissionExceptionType.NonAuthenticate;
            if (Principal == null)
            {
                sb.Append("No user Authentication! please login");
                EventType = PermissionExceptionType.NonAuthenticate;
                throw new BzurePermissionException(sb.ToString(), EventType, this._droit.Copy());
            }
            else if (!Principal.GetPermission().Authentication(_droit))
            {
                sb.AppendFormat("user {0} no rights to excute these code", this.Principal.Identity.Name);
                EventType = PermissionExceptionType.RefuseAccess;
                throw new BzurePermissionException(sb.ToString(), EventType, this._droit.Copy());
            }
            else
            {
                //is ok
            }
        }

        public virtual void FromXml(SecurityElement e)
        {
            if (e == null)
                throw new ArgumentNullException("e");
            if (e.Tag != "BzurePermission")
            {
                throw new ArgumentException("invalid tag type");
            }
            if (!(e.Attributes["class"] as string).StartsWith("Bzure.Security.Permissions.CAS.BzurePermission"))
            {
                throw new ArgumentException("invalid type");
            }
            if ((e.Attributes["version"] as string) != "1")
            {
                throw new ArgumentException("invalid version");
            }
            IDroit droit = new Droit(e.Attributes["guid"] as string);
            if (e.Children != null)
            {
                foreach (SecurityElement se in e.Children)
                {
                    switch (se.Tag)
                    {
                        case "accessType":
                            droit.AddAccessCode(new AccessCode(Convert.ToInt32(se.Text)));
                            break;
                        default:
                            break;
                    }
                }
            }
            this._droit = droit;
        }

        public virtual IPermission Intersect(IPermission target)
        {
            if (target == null)
                return null;

            if (!(target is BzurePermission))
                throw new ArgumentException("target type must is GTCodePermission");
            BzurePermission permission = target as BzurePermission;
            if (this._droit.Guid.CompareTo(permission._droit.Guid) != 0)
                throw new ArgumentException("Must same droit resource");

            if (IsUnrestricted())
                return permission.Copy();
            if (permission.IsUnrestricted())
                return Copy();

            IDroit droit = new Droit(this._droit.Guid.ToString());

            foreach (IAccessCode at in permission._droit.AccessCodes)
                if (this._droit.Contains(at))
                    droit.AddAccessCode(at.Copy());
            return new BzurePermission(droit);
        }

        public virtual bool IsSubsetOf(IPermission target)
        {
            if (target == null)
                return false;
            if (!(target is BzurePermission))
                throw new ArgumentException("target type must is BzurePermission");
            BzurePermission permission = target as BzurePermission;
            if (this._droit.Guid.CompareTo(permission._droit.Guid) != 0)
                throw new ArgumentException("Must same droit resource");

            if (IsUnrestricted())
                return permission.IsUnrestricted();
            else if (permission.IsUnrestricted())
                return true;

            bool contain = true;
            foreach (IAccessCode at in this._droit.AccessCodes)
                contain = contain && permission._droit.Contains(at);
            return contain;
        }

        public bool IsUnrestricted()
        {
            return _isUnrestricted;
        }

        public override string ToString()
        {
            return ToXml().ToString();
        }

        public virtual SecurityElement ToXml()
        {
            SecurityElement se = new SecurityElement("BzurePermission");
            Type type = this.GetType();
            StringBuilder asmName = new StringBuilder(type.Assembly.ToString());
            asmName.Replace('\"', '\'');
            se.AddAttribute("class", type.FullName + ", " + asmName);
            se.AddAttribute("version", "1");
            se.AddAttribute("guid", this._droit.Guid.ToString());

            foreach (IAccessCode at in this._droit.AccessCodes)
            {
                SecurityElement sec = new SecurityElement("accessType");
                sec.Text = at.CodeValue.ToString();
                se.AddChild(sec);
            }
            return se;
        }

        public virtual IPermission Union(IPermission target)
        {
            if (target == null)
                return Copy();
            if (!(target is BzurePermission))
                throw new ArgumentException("target type must is BzurePermission");
            BzurePermission permission = target as BzurePermission;
            if (this._droit.Guid.CompareTo(permission._droit.Guid) != 0)
                //throw new ArgumentException("Must same droit resource");
                return Copy();
            if (IsUnrestricted() || permission.IsUnrestricted())
                return new BzurePermission(PermissionState.Unrestricted);
            IDroit droit = this._droit.Copy();
            foreach (IAccessCode at in permission._droit.AccessCodes)
                droit.AddAccessCode(at);
            return new BzurePermission(droit);
        }
    }
}
